<?php

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

namespace org\copperforest\oak;

use \org\copperforest\oak\sql as sql;

/**
 * Description of oak
 *
 * @author agama
 */
class Oak
{
    static public $pdo;
    static private $resources = array();

    public static function init()
    {

        try {
            
            if ( isset( $_SERVER[ 'CF_INI' ][ 'oak.pdo.connection_string' ] ) ){

                    self::$pdo = new sql\PDO(
                        $_SERVER[ 'CF_INI' ][ 'oak.pdo.connection_string' ],
                        $_SERVER[ 'CF_INI' ][ 'oak.pdo.user' ],
                        $_SERVER[ 'CF_INI' ][ 'oak.pdo.password' ],
                        isset($_SERVER[ 'CF_INI' ][ 'oak.pdo.options' ])?$_SERVER[ 'CF_INI' ][ 'oak.pdo.options' ]:null);

                    self::$pdo->setAttribute( \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION );
            }

            self::$resources[ 'Resources' ] = unserialize( file_get_contents( CF_BUILD_PATH . $_SERVER[ 'CF_BUILD' ] . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'oak.conf' ) );
            
        } catch ( \Exception $ex ) {
            
            \org\copperforest\build\Logger::getLogger( CF_LOG_ERROR )->log( $ex );
            
            $e = new http\exceptions\InternalServerError();
            //var_dump($ex);
            $e->send();
            exit();
            
        }
    }

    public static function pdo(){
        return self::$pdo;
    }
    
    public static function grow()
    {
        try {
            $t0 = $_SERVER[ 'REQUEST_TIME_FLOAT' ];
            $t1 = microtime(true);
            //Un Collection con un POST debería devolver HTTP 201 Created
            //Un borrado correcto debería devolver HTTP 204 No Content

           // var_dump($_SESSION);
            $nextLocation = null; 
            
            if( empty( $_SERVER[ 'PATH_INFO' ] ) || $_SERVER[ 'PATH_INFO' ] == '/' ){
                
                $nextLocation = $_SESSION->getDefaultRelativeReference();
                
                if( empty( $nextLocation ) ){
                    header( $_SERVER['SERVER_PROTOCOL'] . ' 508 Loop Detected' );
                }
                else{
                    header( $_SERVER['SERVER_PROTOCOL'] . ' 303 See other' );
                    header( 'Location: '.  CF_HTTP_BASE . CF_HTTP_PATH . $nextLocation );
                }
                exit();
            }
            
            $referer = null;
            $isRoot = true;
            $inheritGET  = true;
            
            if( !isset( $_SERVER[ 'HTTP_REFERER' ] ) && !isset( $_SERVER[ 'HTTP_HEADERS' ][ 'Oak-Referer' ] )   ){
                
                $nextLocation = CF_HTTP_PATH . ( isset( $_SERVER[ 'ORIG_PATH_INFO'] ) ? $_SERVER[ 'ORIG_PATH_INFO' ] : $_SERVER[ 'PATH_INFO' ] );
                $queryString = '?' . $_SERVER[ 'QUERY_STRING' ];
                $referer = parse_url(  $nextLocation );
                $inheritGET = false;
            }
            else{
                
                if( isset( $_SERVER[ 'HTTP_HEADERS' ][ 'Oak-Referer' ] ) ){
                    
                    $referer = parse_url( $_SERVER[ 'HTTP_HEADERS' ][ 'Oak-Referer' ] ); 
                    $isRoot = ( CF_HTTP_PATH . ( isset( $_SERVER[ 'ORIG_PATH_INFO'] ) ? $_SERVER[ 'ORIG_PATH_INFO' ] : $_SERVER[ 'PATH_INFO' ] ) == $referer[ 'path' ] );
                }
                else{
                    $referer = parse_url( $_SERVER[ 'HTTP_REFERER' ] );
                    $nextLocation = $referer[ 'path' ];
                    $isRoot = true;
                }
                

            }
            
            
            if( isset( $_SERVER[ 'HTTP_HEADERS' ][ 'Accept' ] ) ){
                
                $accepts = explode(',', $_SERVER[ 'HTTP_HEADERS' ][ 'Accept' ] );
                
                foreach($accepts as &$a) {
                    
                    if(  $pyc = strpos( $a, ';' ) ){
                        $a = substr( $a, 0, $pyc );
                    }
                    
                    $a = trim ( $a );
                }
            }
            else {
                $accepts = array( '*/*' );
            }
            
            
            $configuration = new resources\configuration\Configuration( self::$resources );
           
            $t2 = microtime(true);
            $rootConfiguration = $configuration->fetch( $_SERVER[ 'PATH_INFO' ], $isRoot );
            $t3 = microtime(true);
            $root = $rootConfiguration->getResponse( $accepts ); 
            $t4 = microtime(true);
            

            if( in_array( $_SERVER[ 'REQUEST_METHOD' ], array( 'POST', 'PUT', 'PATCH', 'DELETE' ) ) ){
                //tengo un problema con exte nextLocation, en este caso se puede referir al ancestor que es raiz de la página
                //en ese caso, el accept que debería tener la siguiente petición debería cambiar
                
                self::$pdo->beginTransaction();

                try{
                
                    switch ( $_SERVER[ 'REQUEST_METHOD' ] ) {

                        case 'POST':
                                $location = $root->doPost( $_POST[ 'oak_action' ]);
                            break;

                        case 'PUT':
                                $location = $root->doPut();
                            break;

                        case 'PATCH':
                                $location = $root->doPatch();
                            break;

                        case 'DELETE':
                                $location = $root->doDelete();
                            break;
                    }

                    self::$pdo->commit();
                
                }
                catch(\Exception $ex){
                    self::$pdo->rollBack();
                    throw $ex;
                }
            }
                
            $t5 = microtime(true);
                    
            $_SESSION->writeClose();//OJO SI NO HAGO UN COMMIT NO GUARDARÁ LOS DATOS?

            $t6 = microtime(true);
            
            //rootRerence se utiliza en respuesta a cualquier metodo al mostrar llamadas GET en las plantillas
            //se trata de averiguar la referencia al documento root, la  llamada en el POST es siempre a la referencia
            //pero en este caso se tendría que volver al reference
            //lo que esta claro es que ahora tenemos una forma de saber si nos están llamando via javacript o no 

            
            $groundTemplate = null;
            $accept = null;
            list( $groundTemplate, $accept ) = $rootConfiguration->getGroundTemplate( $accepts );
            if( empty( $nextLocation ) ){
                $nextLocation = $location;
            }
            
            if( $inheritGET ){
                
                if( empty( $groundTemplate ) && empty( $nextLocation ) ){
                    header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 204 No Content' );
                    exit();
                }
                    
                parse_str( $referer[ 'query' ], $refererGet );
                
                $newGet = null;
                
                if( empty( $groundTemplate ) ){
                    //AQUI, si el path de $nextLocation es igual al path del referer deberíamos cambiar el accept de la siguiente petición
                    
                    $loc = parse_url( $nextLocation );
                
                    parse_str( $loc[ 'query' ], $newGet );
                }
                else {
                    parse_str( $_SERVER[ 'QUERY_STRING' ], $newGet );
                }
                
                //we must short the query string, we would remove references that don't be backtrace of the $nextLocation ResponseNode, but how?
                
                foreach( $newGet as $ref => $innerGet ){
                    if( !isset( $refererGet[ $ref ] ) ){
                        $refererGet[ $ref ] = array();
                    }
                    
                    foreach( $innerGet as $k => $v ){
                        $refererGet[ $ref ][ $k ] = $v;
                    }
                }
                
                $queryString = '?' . http_build_query( $refererGet );
            
            }
            
            
            resources\Request::$rootGETReference = CF_HTTP_PATH . $referer[ 'path' ];
            $t7 = microtime(true);
            
            if( empty( $groundTemplate ) ){
                
                header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 303 See other' );
                header( 'Location: ' . CF_HTTP_BASE . $nextLocation  );
                header( 'Content-Type: ' . $accept .'; charset=' . $charset );
                
            }
            else{
                
                $locale = $_SERVER[ 'CF_INI' ][ 'intl.default_locale' ];
                $charset = substr( $locale, strrpos( $locale, '.' ) + 1 );
                
                header( 'Content-Type: ' . $accept .'; charset=' . $charset );
                //esto es mentira no tiene porque ser el Oak-Referer el mismo path_info que el actual
                
                header( 'Oak-Referer: '. CF_HTTP_BASE . $referer[ 'path' ] . $queryString );
                $root->setGroundTemplate( $groundTemplate );
                
                $t8 = microtime(true);
                //El HTTP 303 See Other como respuesta a un POST/DELETE/PUT/PATCH se interpreta que hay que hacer un relocation a la misma URL
            
                //echo 'Session Init:' . ($t1-$t0) . '<br/> Configuration default: ' . ($t2-$t1) . '<br />Configuration fetch: ' . ($t3-$t2) . '<br />Get Response: ' . ($t4-$t3) . '<br />Do Post: ' . ($t5-$t4) . '<br />Write Session: ' . ($t6-$t5) . '<br />Get ground Template: ' . ($t7-$t6) . '<br />Show Template: ' . ($t8-$t7);
            }


        } catch ( http\exceptions\HTTPException $ex ) {
            
            \org\copperforest\build\Logger::getLogger( CF_LOG_DEBUG )->log( $ex );
            
            $ex->send();
            
        } catch ( \Exception $ex ) {
            
            \org\copperforest\build\Logger::getLogger( CF_LOG_ERROR )->log( $ex );
            
            $e = new http\exceptions\InternalServerError();
            //var_dump($ex);
            $e->send();
        }
        
        exit();
    }
    
    
}
Oak::init();